package log

import (
	"os"
	"strings"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

const (
	// LogTypeKey 日志类型标识字段，用于容器环境
	LogTypeKey = "log_type"

	// 日志类型值
	LogTypeAccess = "access"
	LogTypeBiz    = "biz"
	LogTypeAudit  = "audit"
	LogTypeSystem = "system"
	LogTypeDebug  = "debug"
	LogTypeAuth   = "auth"
)

var (
	// 各类logger实例
	AccessLogger *zap.Logger
	BizLogger    *zap.Logger
	AuditLogger  *zap.Logger
	SystemLogger *zap.Logger
	DebugLogger  *zap.Logger
	AuthLogger   *zap.Logger
)

// LogConfig 日志配置
type LogConfig struct {
	// 日志级别 (debug, info, warn, error, fatal)
	Level string

	// 是否输出到文件（false则输出到stdout）
	ToFile bool

	// 日志文件路径（当ToFile=true时使用）
	AccessLogPath string
	BizLogPath    string
	AuditLogPath  string
	SystemLogPath string
	DebugLogPath  string
	AuthLogPath   string

	// 是否为容器环境（容器环境会在日志中添加log_type字段并输出到stdout）
	IsContainer bool

	// 是否启用开发模式（更易读的格式）
	Development bool
}

// DefaultConfig 返回默认配置
func DefaultConfig() *LogConfig {
	return &LogConfig{
		Level:         "info",
		ToFile:        false,
		IsContainer:   isRunningInContainer(),
		Development:   false,
		AccessLogPath: "logs/access.log",
		BizLogPath:    "logs/biz.log",
		AuditLogPath:  "logs/audit.log",
		SystemLogPath: "logs/system.log",
		DebugLogPath:  "logs/debug.log",
		AuthLogPath:   "logs/auth.log",
	}
}

// isRunningInContainer 检测是否在容器内运行
func isRunningInContainer() bool {
	// 检测常见的容器环境标识
	if _, err := os.Stat("/.dockerenv"); err == nil {
		return true
	}
	if _, err := os.Stat("/proc/1/cgroup"); err == nil {
		content, err := os.ReadFile("/proc/1/cgroup")
		if err == nil && strings.Contains(string(content), "docker") {
			return true
		}
	}
	// 也可以通过环境变量判断
	if os.Getenv("CONTAINER") == "true" || os.Getenv("KUBERNETES_SERVICE_HOST") != "" {
		return true
	}
	return false
}

// Init 初始化所有logger
func Init(config *LogConfig) error {
	if config == nil {
		config = DefaultConfig()
	}

	level, err := parseLevel(config.Level)
	if err != nil {
		return err
	}

	// 如果配置为容器环境，强制输出到stdout
	if config.IsContainer {
		config.ToFile = false
	}

	// 创建编码器配置
	encoderConfig := zapcore.EncoderConfig{
		TimeKey:        "time",
		LevelKey:       "level",
		NameKey:        "log",
		CallerKey:      "caller",
		MessageKey:     "msg",
		StacktraceKey:  "stacktrace",
		LineEnding:     zapcore.DefaultLineEnding,
		EncodeLevel:    zapcore.LowercaseLevelEncoder,
		EncodeTime:     zapcore.ISO8601TimeEncoder,
		EncodeDuration: zapcore.SecondsDurationEncoder,
		EncodeCaller:   zapcore.ShortCallerEncoder,
	}

	if config.Development {
		encoderConfig = zap.NewDevelopmentEncoderConfig()
		encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
	}

	var encoder zapcore.Encoder
	if config.Development {
		encoder = zapcore.NewConsoleEncoder(encoderConfig)
	} else {
		encoder = zapcore.NewJSONEncoder(encoderConfig)
	}

	// 为每个logger创建writer
	accessWriter := getWriter(config.ToFile, config.AccessLogPath, config.IsContainer, LogTypeAccess)
	bizWriter := getWriter(config.ToFile, config.BizLogPath, config.IsContainer, LogTypeBiz)
	auditWriter := getWriter(config.ToFile, config.AuditLogPath, config.IsContainer, LogTypeAudit)
	systemWriter := getWriter(config.ToFile, config.SystemLogPath, config.IsContainer, LogTypeSystem)
	debugWriter := getWriter(config.ToFile, config.DebugLogPath, config.IsContainer, LogTypeDebug)
	authWriter := getWriter(config.ToFile, config.AuthLogPath, config.IsContainer, LogTypeAuth)

	// 创建core
	accessCore := zapcore.NewCore(encoder, accessWriter, level)
	bizCore := zapcore.NewCore(encoder, bizWriter, level)
	auditCore := zapcore.NewCore(encoder, auditWriter, level)
	systemCore := zapcore.NewCore(encoder, systemWriter, level)
	debugCore := zapcore.NewCore(encoder, debugWriter, zapcore.DebugLevel) // Debug logger总是允许debug级别
	authCore := zapcore.NewCore(encoder, authWriter, level)

	// 创建logger（添加caller信息）
	// 如果是容器环境，为每个logger添加log_type字段
	if config.IsContainer {
		AccessLogger = zap.New(accessCore, zap.AddCaller(), zap.AddCallerSkip(1)).With(zap.String(LogTypeKey, LogTypeAccess))
		BizLogger = zap.New(bizCore, zap.AddCaller(), zap.AddCallerSkip(1)).With(zap.String(LogTypeKey, LogTypeBiz))
		AuditLogger = zap.New(auditCore, zap.AddCaller(), zap.AddCallerSkip(1)).With(zap.String(LogTypeKey, LogTypeAudit))
		SystemLogger = zap.New(systemCore, zap.AddCaller(), zap.AddCallerSkip(1)).With(zap.String(LogTypeKey, LogTypeSystem))
		DebugLogger = zap.New(debugCore, zap.AddCaller(), zap.AddCallerSkip(1)).With(zap.String(LogTypeKey, LogTypeDebug))
		AuthLogger = zap.New(authCore, zap.AddCaller(), zap.AddCallerSkip(1)).With(zap.String(LogTypeKey, LogTypeAuth))
	} else {
		AccessLogger = zap.New(accessCore, zap.AddCaller(), zap.AddCallerSkip(1))
		BizLogger = zap.New(bizCore, zap.AddCaller(), zap.AddCallerSkip(1))
		AuditLogger = zap.New(auditCore, zap.AddCaller(), zap.AddCallerSkip(1))
		SystemLogger = zap.New(systemCore, zap.AddCaller(), zap.AddCallerSkip(1))
		DebugLogger = zap.New(debugCore, zap.AddCaller(), zap.AddCallerSkip(1))
		AuthLogger = zap.New(authCore, zap.AddCaller(), zap.AddCallerSkip(1))
	}

	return nil
}

// parseLevel 解析日志级别
func parseLevel(level string) (zapcore.Level, error) {
	switch strings.ToLower(level) {
	case "debug":
		return zapcore.DebugLevel, nil
	case "info":
		return zapcore.InfoLevel, nil
	case "warn", "warning":
		return zapcore.WarnLevel, nil
	case "error":
		return zapcore.ErrorLevel, nil
	case "fatal":
		return zapcore.FatalLevel, nil
	default:
		return zapcore.InfoLevel, nil
	}
}

// getWriter 获取日志writer
func getWriter(toFile bool, filePath string, isContainer bool, logType string) zapcore.WriteSyncer {
	// 容器环境总是输出到stdout
	if isContainer || !toFile {
		return zapcore.AddSync(os.Stdout)
	}

	// 文件输出：确保目录存在
	if err := os.MkdirAll(filePath[:strings.LastIndex(filePath, "/")], 0755); err != nil {
		// 如果创建目录失败，回退到stdout
		return zapcore.AddSync(os.Stdout)
	}

	file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		// 如果打开文件失败，回退到stdout
		return zapcore.AddSync(os.Stdout)
	}

	return zapcore.AddSync(file)
}

// Sync 同步所有logger
func Sync() {
	if AccessLogger != nil {
		_ = AccessLogger.Sync()
	}
	if BizLogger != nil {
		_ = BizLogger.Sync()
	}
	if AuditLogger != nil {
		_ = AuditLogger.Sync()
	}
	if SystemLogger != nil {
		_ = SystemLogger.Sync()
	}
	if DebugLogger != nil {
		_ = DebugLogger.Sync()
	}
	if AuthLogger != nil {
		_ = AuthLogger.Sync()
	}
}
